package com.yuanli.common.aspect;

import com.yuanli.common.aspect.annotation.PermissionData;
import com.yuanli.common.constant.CommonConstant;
import com.yuanli.common.exception.YuanliBootException;
import com.yuanli.common.utils.RedisUtil;
import com.yuanli.common.utils.SpringContextUtils;
import com.yuanli.common.utils.oConvertUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

/**
 * 权限数据切面处理类
 * 此类通过AOP方式实现权限数据的拦截与验证功能。
 */
@Aspect
@Component
@Slf4j
public class PermissionDataAspect {

    /**
     * 注入Redis工具类，用于读取存储在Redis中的用户权限数据。
     */
    @Autowired
    private RedisUtil redisUtil;

    /**
     * 定义一个切入点，匹配带有自定义注解PermissionData的方法。
     */
    @Pointcut("@annotation(com.yuanli.common.aspect.annotation.PermissionData)")
    public void pointCut() {
        // 切点方法体为空，用于定义切入点表达式
    }

    /**
     * 环绕通知，用于在目标方法执行前后进行权限验证。
     *
     * @param point ProceedingJoinPoint对象，提供对目标方法的调用能力
     * @return 目标方法的执行结果
     * @throws Throwable 当目标方法抛出异常时，此异常将被抛出
     */
    @Around("pointCut()")
    public Object arround(ProceedingJoinPoint point) throws Throwable {
        // 获取当前请求对象
        HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
        // 获取方法签名
        MethodSignature signature = (MethodSignature) point.getSignature();
        // 获取目标方法
        Method method = signature.getMethod();

        // 获取目标方法上的PermissionData注解
        PermissionData pd = method.getAnnotation(PermissionData.class);
        // 获取注解的value属性值
        String value = pd.value();

        // 如果value非空，则进行权限验证
        if (oConvertUtils.isNotEmpty(value)) {
            // 从请求头或参数中获取token
            String token = oConvertUtils.isEmpty(request.getHeader(CommonConstant.X_ACCESS_TOKEN))
                    ? request.getParameter("token")
                    : request.getHeader(CommonConstant.X_ACCESS_TOKEN);

            // 从Redis中获取用户权限信息
            String auths = (String) redisUtil.get(CommonConstant.Login_User_Vo + token);

            // 如果未找到权限信息，提示token失效
            if (oConvertUtils.isEmpty(auths)) {
                log.warn(" ------ PermissionDataAspect ------ token失效，请重新登录 ------ ");
                throw new YuanliBootException("没有权限，请联系管理员授权");
            }

            // 验证用户权限是否包含目标方法所需的权限
            List<String> authsList = Arrays.asList(auths.split(","));
            if (authsList.contains(value)) {
                // 权限验证通过，允许执行目标方法
                return point.proceed();
            }

            // 权限验证失败，抛出异常
            log.warn(" ------ PermissionDataAspect ------ 无权限访问接口 ------ ");
            throw new YuanliBootException("没有权限，请联系管理员授权");
        }

        // 如果value为空，直接执行目标方法
        return point.proceed();
    }

}
